﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Prologis.Logging;
using Prologis.Shifter.Extensions;
using System.Collections;


namespace Prologis.Shifter.Unity
{
    public static class Builder
    {
        static Logger log = new Logger(MethodBase.GetCurrentMethod().DeclaringType.Namespace);


        /// <summary>
        /// Builds the shifter.
        /// </summary>
        /// <param name="unityConfigpath">The unity configpath.</param>
        /// <returns></returns>
        public static DefaultAppender BuildShifter(string unityConfigpath)
        {
            log.Verbose("BuildShifter runs with UnityConfigpath: " + unityConfigpath + ".");

            IUnityContainer container = new UnityContainer();
            UnityConfigurationSection section = GetUnityConfigurationSection(unityConfigpath);
            section.Containers.Default.Configure(container);
            return BuildShifter(container);
        }


        /// <summary>
        /// Tries the build shifter.
        /// </summary>
        /// <param name="unityConfigpath">The unity configpath.</param>
        /// <param name="defaultAppender">The default appender.</param>
        /// <returns></returns>
        public static bool TryBuildShifter(string unityConfigpath, DefaultAppender defaultAppender)
        {
            bool result = true;

            try
            {
                defaultAppender = BuildShifter(unityConfigpath);
            }
            catch (Exception e)
            {
                log.Exception(e, "TryBuildShifter failed! UnityConfigpath: " + unityConfigpath + ".");
                result = false;
            }

            return result;
        }



        /// <summary>
        /// Builds the shifter.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <returns></returns>
        public static DefaultAppender BuildShifter(IUnityContainer container)
        {
            DefaultAppender defaultAppender = container.Resolve<DefaultAppender>();
            string fieldConfigPath = defaultAppender.DataSource.Tokenizer.FieldConfigPath;
            
            XmlDocument xmlDoc = new XmlDocument();
			xmlDoc.Load(fieldConfigPath);
            XmlElement root = xmlDoc.DocumentElement;
            XmlElement fieldsElement = (XmlElement)root.SelectSingleNode("/fields");
            FieldList fieldList = ParseFieldsStatic(fieldsElement);

            defaultAppender.DataSource.Tokenizer.Fields = fieldList;
            log.Verbose("BuildShifter succeeded.");

            return defaultAppender;
        }

        


        /// <summary>
        /// Tries the build shifter.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="defaultAppender">The default appender.</param>
        /// <returns></returns>
        public static bool TryBuildShifter(IUnityContainer container, DefaultAppender defaultAppender)
        {
            bool result = true;

            try
            {
                defaultAppender = BuildShifter(container);
            }
            catch (Exception e)
            {
                log.Exception(e, "TryBuildShifter failed!");
                result = false;
            }

            return result;
        }



        /// <summary>
        /// Gets the unity configuration section.
        /// </summary>
        /// <param name="unityConfigpath">The unity configpath.</param>
        /// <returns></returns>
        public static UnityConfigurationSection GetUnityConfigurationSection(string unityConfigpath)
        {
            ExeConfigurationFileMap map = new ExeConfigurationFileMap();
            map.ExeConfigFilename = unityConfigpath;

            System.Configuration.Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
            UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection("unity");

            log.Info("Unity Builder success: GetUnityConfigurationSection = " + configuration);

            return section;
        }




        /// <summary>
        /// Tries the get unity configuration section.
        /// </summary>
        /// <param name="unityConfigpath">The unity configpath.</param>
        /// <param name="unityConfigSection">The unity config section.</param>
        /// <returns></returns>
        public static bool TryGetUnityConfigurationSection(string unityConfigpath, UnityConfigurationSection unityConfigSection)
        {
            bool result = true;

            try
            {
                unityConfigSection = GetUnityConfigurationSection(unityConfigpath);
            }
            catch (Exception e)
            {
                log.Exception(e, "TryGetUnityConfigurationSection failed! UnityConfigpath: " + unityConfigpath);
                result = false;
            }

            return result;

        }

               


        public static FieldList ParseFieldsStatic(XmlElement fieldsElement)
        {


            FieldList fields = new FieldList();

            if (fieldsElement.ChildNodes.Count == 0)
            {

                string countObject = fieldsElement.GetAttribute("1");
                string externalObject = fieldsElement.GetAttribute("2");

                if (externalObject == string.Empty && countObject != string.Empty)
                {
                    int fieldCount = int.Parse(countObject.ToString());
                    for (int i = 0; i < fieldCount; i++)
                    {
                        Field field = new Field(i, "Field" + i);
                        field.IsKey = false;
                        field.Source = fieldsElement.GetAttribute("source");
                        fields.Add(field);
                    }
                    fields.HasStrongNames = false;
                }
                else
                {
                    throw new Exception("Error in Fieldsdefinition!");
                }

            }

            for (int i = 0; i < fieldsElement.ChildNodes.Count; i++)
            {
                XmlNode node = fieldsElement.ChildNodes[i];
                if (node.NodeType == XmlNodeType.Element)
                {
                    Field field = GetFieldFromElementStatic((XmlElement)node);
                    if (field.Index == -1)
                    {
                        field.Index = i;
                    }
                    if (field.Source == String.Empty)
                    {
                        field.Source = "Main";
                    }
                    fields.Add(field);
                }
            }


            return fields;

        }


        private static Field GetFieldFromElementStatic(XmlElement element)
        {

            Field field = new Field();

            foreach (XmlNode attribute in element.Attributes)
            {

                switch (attribute.Name.ToLower())
                {

                    case "name":
                        //field.Name = attribute.Value.ToUpper();
                        field.Name = attribute.Value;
                        break;

                    case "source":
                        field.Source = attribute.Value;
                        break;

                    case "pos":
                        field.Pos = int.Parse(attribute.Value);
                        break;

                    case "length":
                        field.Length = int.Parse(attribute.Value);
                        break;

                    case "key":
                        field.IsKey = bool.Parse(attribute.Value);
                        break;

                    case "enabled":
                        field.Enabled = bool.Parse(attribute.Value);
                        break;

                    case "index":
                        field.Index = int.Parse(attribute.Value);
                        break;

                    case "isstring":
                        field.IsString = bool.Parse(attribute.Value);
                        break;

                    case "default":
                        field.Default = attribute.Value;
                        field.Value = field.Default;
                        break;

                }

            }

            return field;
        }

        

        /// <summary>
        /// Loads the specified config path.
        /// </summary>
        /// <param name="configPath">The config path.</param>
        public static IEnumerable<XElement> LoadFieldsFromFile(string configPath)
        {
            string fileContent = File.ReadAllText(configPath);
            XElement rootElement = XElement.Parse(fileContent);
            var fields = from f in rootElement.Elements() where f.Name == "field" select f;
            return fields;
        }



        public static FieldList aBuildFieldList(string unityConfigpath)
        {
            var fields = LoadFieldsFromFile(unityConfigpath);
            FieldList fieldList = BuildFieldList(fields);
            return fieldList;

        }


        public static FieldList BuildFieldList(IEnumerable<XElement> fields)
        {
            FieldList result = new FieldList();
            foreach (var fieldElement in fields)
            {
                var attributes = from a in fieldElement.Attributes() select a;
                Field field = GetFieldFromAttributes(attributes);
                result.Add(field);
            }
            return result;
        }


        private static Field GetFieldFromAttributes(IEnumerable<XAttribute> attributes)
        {
            Field field = new Field();

            foreach (var attribute in attributes)
            {
                switch (attribute.Name.ToString().ToLower())
                {
                    case "name":
                        //field.Name = attribute.Value.ToUpper();
                        field.Name = attribute.Value;
                        break;

                    case "source":
                        field.Source = attribute.Value;
                        break;

                    case "pos":
                        field.Pos = int.Parse(attribute.Value);
                        break;

                    case "length":
                        field.Length = int.Parse(attribute.Value);
                        break;

                    case "key":
                        field.IsKey = bool.Parse(attribute.Value);
                        break;

                    case "enabled":
                        field.Enabled = bool.Parse(attribute.Value);
                        break;

                    case "index":
                        field.Index = int.Parse(attribute.Value);
                        break;

                    case "isstring":
                        field.IsString = bool.Parse(attribute.Value);
                        break;

                    case "default":
                        field.Default = attribute.Value;
                        field.Value = field.Default;
                        break;
                }

            }

            return field;
        }





    }

}
